/*Copyright 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
var jsonrpc_server = (function() {
    
    function isArray(obj) {
        return obj.constructor.toString().indexOf("Array") !== -1;
    }

    return function() {
        var registeredMethods = {};
    
        function processMessage(message, meta, responder) {
            if (!message.jsonrpc || message.jsonrpc !== "2.0") { throw "invalid rpc call"; }
            if (typeof message.method !== "string") { throw "invalid rpc call"; }
            if (!isArray(message.params)) { throw "invalid rpc call"; }
            if (typeof message.id === "undefined") { message.id = null; }

            if (!registeredMethods[message.method]) { throw "rpc method doesn't exist"; }
        
            var methodInfo = registeredMethods[message.method];
        
            var callback = function(error, result) {
                if (message.id && responder) {
                    if (!error) {
                        responder({
                            jsonrpc: "2.0",
                            result: result,
                            id: message.id
                        });
                    } else {
                        responder({
                            jsonrpc: "2.0",
                            error: result,
                            id: message.id
                        });
                    }
                    
                }
                //responder = null; // allow-subscriptions
            };
            meta.callback = function (result) { callback(false, result); };
			meta.errorback = function (result) { callback(true, result); };
        
            var params = message.params;
            if (methodInfo.includeMeta) {
                params.splice(0, 0, meta);
            }
            try {
                var result = methodInfo.func.apply(methodInfo.context, params);
                if (typeof result !== "undefined") { callback(false, result); }
            } catch(e) {
                callback(true, e);
            }
        }
    
        var jsonrpc_obj = {};
        jsonrpc_obj.register = function(name, func, context, includeMeta) {
            if (registeredMethods[name]) { return false; }
            if (typeof name !== "string") { throw "string expected as first param to register()"; }
            if (typeof func !== "function") { throw "function expected as second param to register()"; }
        
            registeredMethods[name] = {
                name: name,
                func: func,
                context: context,
                includeMeta: includeMeta ? true : false
            };
            return true;
        };
        
        jsonrpc_obj.unregister = function(name) {
            delete registeredMethods[name];
        };
        
        jsonrpc_obj.addMessageReceiver = function(provider) {
            provider(processMessage);
        };
    
        return jsonrpc_obj;
    };
}());

/*global window*/

function getPostMessageReceiver(origin, win) {
    return function (messageCallback) {
        var data;
        if (typeof JSON === "undefined" || typeof JSON.stringify === "undefined" || typeof JSON.parse === "undefined") {
            throw "jsonrpc requires the JSON library";
        }
		
        var messageHandler = function(e) {
			if (typeof win !== "undefined" && win !== e.source) {
				return;
			}
            if (typeof origin !== "undefined" && origin !== "*" && origin !== e.origin) {
                return;
            }
            try {
                data = JSON.parse(e.data);
            } catch(error) {
                throw "error parsing json";
            }
            var meta = {origin: e.origin, source: e.source};
            messageCallback(data, meta, function(response) {
                e.source.postMessage(JSON.stringify(response), e.origin);
            });
        };
		if (window.addEventListener) {
			window.addEventListener("message", messageHandler, false);
		} else if (window.attachEvent) {
			window.attachEvent("onmessage", messageHandler);
		}
    };
}